use clap::{Subcommand, ValueEnum};
use std::io;
use std::path::{Path, PathBuf};

pub mod device_conf;
pub mod project_conf;
pub mod train_conf;

use self::project_conf::DevicePoint;

use super::Project;
use device_conf::DeviceConf;
use project_conf::{parse_key_val, ProjectConf, AppPageCaptureItemValue};
use train_conf::TrainConf;

#[derive(ValueEnum, Copy, Clone, Debug, PartialEq, Eq)]
pub enum ConfigObject {
  AppPage,
}

#[derive(Debug, Subcommand)]
pub enum ProjectConfigCommands {
  Set {
    #[arg(short='C',
      long,
      default_value_t = ConfigObject::AppPage,
       value_enum)]
    config_object: ConfigObject,

    page: String,

    #[arg(short, value_parser=parse_key_val::<String, DevicePoint>, number_of_values = 1)]
    taps: Vec<(String, DevicePoint)>,

    // #[arg(short, value_parser=parse_key_val::<String, DeviceShape>, number_of_values = 1)]
    // shapes: Vec<(String, DeviceShape)>,
    #[arg(short, value_parser=parse_key_val::<String, AppPageCaptureItemValue>, number_of_values = 1)]
    capture: Vec<(String, AppPageCaptureItemValue)>
  },
}

pub fn serve_commands(proj: &mut Project, command: &ProjectConfigCommands) {
  match command {
    ProjectConfigCommands::Set {
      config_object,
      page,
      taps,
      capture,
    } => match config_object {
      ConfigObject::AppPage => {
        proj.apply_to_page(page.to_string(), taps, capture);
      }
    },
  }
}

const PROJ_CONFIG_FILE: &str = "hermes.toml";

pub trait Tomlable<'a, P>
where
  Self: Sized,
{
  fn write_to_toml(&self, to_location: P, overridable: bool) -> io::Result<()>;
  fn read_from_toml(from_location: P) -> io::Result<Self>;
}

pub fn write_project_conf_to_toml<P>(
  project_dir: P,
  proj_conf: &ProjectConf,
  overridable: bool,
) -> io::Result<()>
where
  P: AsRef<Path>,
{
  let mut to_location = PathBuf::new();
  to_location.push(project_dir.as_ref());
  to_location.push(PROJ_CONFIG_FILE);
  proj_conf.write_to_toml(to_location, overridable)
}

pub fn read_project_conf_from_toml<P>(project_dir: P) -> io::Result<ProjectConf>
where
  P: AsRef<Path>,
{
  let mut from_location = PathBuf::new();
  from_location.push(project_dir.as_ref());
  from_location.push(PROJ_CONFIG_FILE);
  ProjectConf::read_from_toml(from_location)
}

pub fn write_device_conf_to_toml<P>(
  project_dir: P,
  device_conf: &DeviceConf,
  overridable: bool,
) -> io::Result<()>
where
  P: AsRef<Path>,
{
  let mut to_location = PathBuf::new();
  to_location.push(project_dir.as_ref());
  to_location.push(format!("{}.toml", device_conf.sn));
  device_conf.write_to_toml(to_location, overridable)
}

pub fn read_device_conf_from_toml<P>(project_dir: P, sn: &str) -> io::Result<DeviceConf>
where
  P: AsRef<Path>,
{
  let mut from_location = PathBuf::new();
  from_location.push(project_dir.as_ref());
  from_location.push(format!("{}.toml", sn));
  DeviceConf::read_from_toml(from_location)
}

pub fn write_train_conf_to_toml<P>(
  base_dir: P,
  train_conf: &TrainConf,
  overridable: bool,
) -> io::Result<()>
where
  P: AsRef<Path>,
{
  let mut to_location = PathBuf::new();
  to_location.push(train_conf.dir(base_dir));
  to_location.push("train.toml");
  train_conf.write_to_toml(to_location, overridable)
}

pub fn read_train_conf_from_toml<P>(base_dir: P, lang: &str, font: &str) -> io::Result<TrainConf>
where
  P: AsRef<Path>,
{
  let mut from_location = PathBuf::new();
  from_location.push(TrainConf::resolve_dir(base_dir, lang, font));
  from_location.push("train.toml");
  TrainConf::read_from_toml(from_location)
}
